package com.zc.common.globalid;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * <p>Description : </p>
 *
 * @author : xuhongcao
 * @version : v1.0.0
 * @since : 15/4/2022
 **/

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class GlobalGeneratorMybatisFilter implements Interceptor {
    private static final String SETTER_METHOD_PREFIX = "set";
    private static final String GETTER_METHOD_PREFIX = "get";

    @Autowired
    GlobalIdService globalIdService;

    private static final Logger logger = LoggerFactory.getLogger(GlobalGeneratorMybatisFilter.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        if (mappedStatement == null) {
            return invocation.proceed();
        }
        if (SqlCommandType.INSERT != mappedStatement.getSqlCommandType()) {
            return invocation.proceed();
        }
        Object parameter = null;
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }
        Class voCls = parameter.getClass();
        if (voCls.isAnnotationPresent(GlobalIdGenerator.class)) {
            Field field = getIdField(voCls);
            if (field == null) {
                return invocation.proceed();
            }
            logger.info("system generator id");
            String getterMethodName = GETTER_METHOD_PREFIX + StringUtils.capitalize(field.getName());
            String setterMethodName = SETTER_METHOD_PREFIX + StringUtils.capitalize(field.getName());
            Method getterMethod = voCls.getMethod(getterMethodName);
            Method setterMethod = voCls.getMethod(setterMethodName, field.getType());
            GlobalId globalId = field.getAnnotation(GlobalId.class);

            if (getterMethod.invoke(parameter) == null) {
                setterMethod.invoke(parameter, globalIdService.getAutoId(globalId.key(), globalId.idType(), globalId.length()));
            }
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object o) {
        return null;
    }

    @Override
    public void setProperties(Properties properties) {

    }

    protected Field getIdField(Class cls) {
        if (cls == null) {
            return null;
        }
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(GlobalId.class)) {
                return field;
            }
        }
        return null;
    }
}
